package StratelegoGame;
import java.util.Random;

import SwingLib.Message;
import TwoClientGameConnection.TCGGeneralException;
import TwoClientGameConnection.TCGMessage;
import TwoClientGameConnection.TCGServer;
import TwoClientGameConnection.TCGServerProtocol;
import TwoClientGameConnection.TCGServerThread;

/**
 * This class defines the protocol for messages sent
 * between the client and the server.
 * @author Jace Ferguson
 * @filename ServerProtocol.java
 *
 */
public class ServerProtocol implements TCGServerProtocol{
	public static int HOST_ACTIVE = 0;
	public static int HOST_WAITING = 1;
	public static int HOST_WAITING_TO_START = 2;
	public static int HOST_SETUP = 3;

	private TCGServer server;
	private String name;

	/**
	 * Construct the server using a default port.
	 */
	public ServerProtocol()
	{
		Thread serverStart = new Thread(this.server = new TCGServer());
		this.server.setProtocol(this);
		serverStart.setDaemon(true);
		serverStart.start();
	}

	/**
	 * Construct the server using a specified port.
	 * @param gameport	Integer
	 */
	public ServerProtocol(Integer gameport)
	{
		Thread serverStart = new Thread(this.server = new TCGServer(gameport));
		this.server.setProtocol(this);
		serverStart.setDaemon(true);
		serverStart.start();
	}

	/**
	 * Check to see if both players are ready to play.
	 * @return	boolean
	 */
	public boolean checkStartStatus()
	{
		TCGServerThread player1 = this.server.getPlayer1();
		TCGServerThread player2 = this.server.getPlayer2();
		if(player1 != null && player2 != null)
		{
			if(player1.getStatus() == ServerProtocol.HOST_WAITING_TO_START && player2.getStatus() == ServerProtocol.HOST_WAITING_TO_START)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		return false;
	}

	/**
	 * Start the clients playing the game.
	 */
	public void commencePlay()
	{
		if(this.server != null)
		{
			//Change the player thread status.
			this.server.getPlayer1().setStatus(ServerProtocol.HOST_WAITING);
			this.server.getPlayer2().setStatus(ServerProtocol.HOST_WAITING);
			//Create a commence play message
			TCGMessage commencePlay = new TCGMessage(this.server.getMessageId());
			commencePlay.setCommand("commencePlay");
			//Send the message to all the players.
			this.server.sendMessageToAllPlayers(commencePlay);
			//Create a message for the staring player
			TCGMessage playerFirstStart = new TCGMessage(this.server.getMessageId());
			playerFirstStart.setCommand("firstMove");
			playerFirstStart.addParameter("startMessage", "You move first.");
			//Create a message for the non starting player
			TCGMessage playerSecondStart = new TCGMessage(this.server.getMessageId());
			playerSecondStart.setCommand("firstMove");
			playerSecondStart.addParameter("startMessage", "You move second.");
			//Create the active turn message
			TCGMessage activeTurn = new TCGMessage(this.server.getMessageId());
			activeTurn.setCommand("yourTurn");
			//Create a random number to determine who goes first.
			Random rand = new Random();
			if(rand.nextInt(1) == 0) //Player 1 goes first.
			{
				this.server.getPlayer1().sendMessage(playerFirstStart);
				this.server.getPlayer1().setStatus(ServerProtocol.HOST_ACTIVE);
				this.server.getPlayer1().sendMessage(activeTurn);
				this.server.getPlayer2().sendMessage(playerSecondStart);
			}
			else	//Player 2 goes first
			{
				this.server.getPlayer1().sendMessage(playerSecondStart);
				this.server.getPlayer2().setStatus(ServerProtocol.HOST_ACTIVE);
				this.server.getPlayer2().sendMessage(playerFirstStart);
				this.server.getPlayer2().sendMessage(activeTurn);
			}
		}
		else
			this.handleError(new TCGGeneralException("Server unavailable. From: commencePlay"));
	}

	/**
	 * Get the name of this protocol
	 * @return String
	 */
	public String getProtocolName()
	{
		return this.name;
	}

	/**
	 * Handle any exceptions on the server by show a message.
	 */
	public void handleError(TCGGeneralException e)
	{
		new Message("Server Exception: " + e, "Server Exception", Message.WARNING);
	}

	/**
	 * Main control structure for the server.
	 */
	public void handleMessage(TCGMessage message, String host)
	{
		//A chat message was received
		if(message.getCommand().equals("chatMessage"))
		{
			this.receiveChatMessage(message, host);
		}
		//Request to change client alias
		else if(message.getCommand().equals("CHANGEALIAS"))
		{
			this.receiveChangeAlias(message, host);
		}
		//Signal that client is ready to start a new game
		else if(message.getCommand().equals("readyToStartNewGame"))
		{
			this.receiveReadyToStartNewGame(message, host);
		}
		//Signal that the client is waiting to start game play
		else if(message.getCommand().equals("waitingToBegin"))
		{
			this.receiveHostWaitingToBegin(message, host);
		}
		//Client sends an attack message to opponent
		else if(message.getCommand().equals("sendAttackMessageToOpponent"))
		{
			this.receiveAttackMessageToOpponent(message, host);
		}
		//Client requests opponent remove piece from space with space id.
		else if(message.getCommand().equals("removePieceWithId"))
		{
			this.receiveRemoveCommandToOpponent(message, host);			 			 
		}
		//Client requests a piece be moved from source to target spaces given space ids.
		else if(message.getCommand().equals("movePieceWithIds"))
		{
			this.receiveMoveCommandToOpponent(message, host);
		}
		//Add a piece with a specified value to a space given a space id
		else if(message.getCommand().equals("addPieceWithValueToSpace"))
		{
			this.receiveAddPieceCommandToOpponent(message, host);
		}
		//Signal that the game is over.
		else if(message.getCommand().equals("gameOver"))
		{
			this.receiveEndGameCommandToOpponent(message, host);
		}
		//Send a game result message to opponent
		else if(message.getCommand().equals("retrieveResultMessage"))
		{
			this.receiveResultMessage(message, host);
		}
		//Send turn switch to opponent
		else if(message.getCommand().equals("yourTurn"))
		{
			this.receiveStartTurnMessage(message, host);
		}
		//Send a player check message to host.
		else if(message.getCommand().equals("checkForAllPlayers"))
		{
			this.receiveCheckPlayers(message, host);
		}
	}

	/**
	 * Identify the opposite thread given a thread's host. 
	 * @param host	String
	 * @return TCGServerThread
	 */
	public TCGServerThread identifyOppositeRecipient(String host)
	{
		if(this.server.getPlayer1().getHost().equals(host))
		{
			return this.server.getPlayer2();
		}
		else if(this.server.getPlayer2().getHost().equals(host))
		{
			return this.server.getPlayer1();
		}
		return null;
	}

	/**
	 * Identify a host thread given it's host string.
	 * @param host	String
	 * @return	TCGServerThread
	 */
	public TCGServerThread identifyRecipient(String host)
	{
		if(this.server.player1Isset() && this.server.getPlayer1().getHost().equals(host))
		{
			return this.server.getPlayer1();
		}
		else if(this.server.player2Isset() && this.server.getPlayer2().getHost().equals(host))
		{
			return this.server.getPlayer2();
		}
		System.err.println("Host unidentifiable.");
		return null;
	}

	/**
	 * Attempt to shut down the server.
	 */
	public void killServer()
	{
		this.server.killServer();
	}

	/**
	 * Request that a opponent add a piece given a value.
	 * @param message	TCGMessage
	 * @param host	String
	 */
	public void receiveAddPieceCommandToOpponent(TCGMessage message, String host)
	{
		if(this.server != null)
		{
			//Must replace the message id with a server defined one.
			message.setMessageId(this.server.getMessageId());
			this.identifyOppositeRecipient(host).sendMessage(message);
		}
		else
			this.handleError(new TCGGeneralException("Server unavailable. From: receiveAddPieceCommandToOpponent"));
	}

	/**
	 * Send an attack message to an opponent.
	 * @param message
	 * @param host
	 */
	public void receiveAttackMessageToOpponent(TCGMessage message, String host)
	{
		if(this.server != null)
		{
			TCGMessage attackMessage = new TCGMessage(this.server.getMessageId());
			attackMessage.setCommand("receiveAttackMessage");
			attackMessage.addParameter("attackMessage", message.getParameterValue("attackMessage"));
			this.identifyOppositeRecipient(host).sendMessage(attackMessage);
		}
		else
			this.handleError(new TCGGeneralException("Server unavailable. From: receiveAttackMessageToOpponent."));
	}

	/**
	 * Receive a request to change the alias. Alert all players
	 * of the change with a chat message.
	 * @param message	TCGMessage
	 * @param host	String
	 */
	public void receiveChangeAlias(TCGMessage message, String host)
	{
		if(this.server != null)
		{
			TCGMessage sendChat = new TCGMessage(this.server.getMessageId());
			String chatMessage = "<" + this.server.aliasLookup(host) + "> " +host + " now known as " + message.getParameterValue("alias");
			sendChat.setCommand("chatMessage");
			sendChat.setMessageType(TCGMessage.MESSAGE_TYPE_LOW);
			sendChat.addParameter("message", chatMessage);
			this.server.sendMessageToAllPlayers(sendChat);
		}
		else
			this.handleError(new TCGGeneralException("Server unavailable. From: receiveChangeAlias"));
	}

	/**
	 * Receive a chat message and distribute it to all players.
	 * @param message	TCGMessage
	 * @param host	String
	 */
	public void receiveChatMessage(TCGMessage message, String host)
	{
		if(this.server != null)
		{
			TCGMessage sendChat = new TCGMessage(this.server.getMessageId());
			String chatMessage = "<" + this.server.aliasLookup(host) + "> " + message.getParameterValue("message");
			sendChat.setCommand("chatMessage");
			sendChat.setMessageType(TCGMessage.MESSAGE_TYPE_LOW);
			sendChat.addParameter("message", chatMessage);
			this.server.sendMessageToAllPlayers(sendChat);
		}
		else
			this.handleError(new TCGGeneralException("Server unavailable. From: receiveChatMessage"));
	}

	/**
	 * Check that two players are connected to the server
	 * and let the host know.
	 * @param message	TCGMessage
	 * @param host	host
	 */
	public void receiveCheckPlayers(TCGMessage message, String host)
	{
		if(this.server != null)
		{
			TCGMessage playerCheck = new TCGMessage(this.server.getMessageId());
			playerCheck.setCommand("playerCheck");
			if(this.server.player1Isset() && this.server.player2Isset())
				playerCheck.addParameter("playerCheck", String.valueOf(1));
			else
				playerCheck.addParameter("playerCheck", String.valueOf(0));
			this.identifyRecipient(host).sendMessage(playerCheck);
		}
		else
			this.handleError(new TCGGeneralException("Server unavailable. From: receiveCheckPlayers"));

	}

	/**
	 * Receive an end game command and forward to opponent.
	 * @param message	TCGMessage
	 * @param host	String
	 */
	public void receiveEndGameCommandToOpponent(TCGMessage message, String host)
	{
		if(this.server != null)
		{
			message.setMessageId(this.server.getMessageId());
			this.identifyOppositeRecipient(host).sendMessage(message);
		}
		else
			this.handleError(new TCGGeneralException("Server unavailable. From: receiveEndGameCommandToOpponent"));
	}

	/**
	 * Receive a host waiting command and check to see if both hosts are waiting.
	 * @param message	TCGMessage
	 * @param host	String
	 */
	public void receiveHostWaitingToBegin(TCGMessage message, String host)
	{
		TCGServerThread serverThread = this.identifyRecipient(host);
		if(serverThread != null)
		{
			serverThread.setStatus(ServerProtocol.HOST_WAITING_TO_START);
			if(this.checkStartStatus())
			{
				this.commencePlay();
			}
		}
	}

	/**
	 * Forward a command to move a piece to an opponent
	 * @param message	TCGMessage
	 * @param host	String
	 */
	public void receiveMoveCommandToOpponent(TCGMessage message, String host)
	{
		if(this.server != null)
		{
			message.setMessageId(this.server.getMessageId());
			this.identifyOppositeRecipient(host).sendMessage(message);
		}
		else
			this.handleError(new TCGGeneralException("Server unavailable. From: receiveMoveCommandToOpponent"));
	}

	/**
	 * Receive a command that the host is ready to start a game.
	 * @param message	TCGMessage
	 * @param host	String
	 */
	public void receiveReadyToStartNewGame(TCGMessage message, String host)
	{
		if(this.server != null)
		{
			TCGMessage startNewGame = new TCGMessage(this.server.getMessageId());
			startNewGame.setCommand("startNewGame");
			TCGServerThread hostThread = this.identifyRecipient(host);
			System.out.println("Starting new game on host: " + host);
			if(hostThread != null)
			{
				hostThread.sendMessage(startNewGame);
				hostThread.setStatus(ServerProtocol.HOST_SETUP);
			}		 
		}
		else
			this.handleError(new TCGGeneralException("Server unavailable. From: receiveReadyToStartNewGame"));
	}

	/**
	 * Send a remove piece message to opponent
	 * @param message	TCGMessage
	 * @param host	String
	 */
	public void receiveRemoveCommandToOpponent(TCGMessage message, String host)
	{
		if(this.server != null)
		{
			message.setMessageId(this.server.getMessageId());
			this.identifyOppositeRecipient(host).sendMessage(message);
		}
		else
			this.handleError(new TCGGeneralException("Server unavailable. From: receiveRemoveCommandToOpponent"));
	}

	/**
	 * Send a game result message to the client
	 * @param message	TCGMessage
	 * @param host	String
	 */
	public void receiveResultMessage(TCGMessage message, String host)
	{
		if(this.server != null)
		{
			message.setMessageId(this.server.getMessageId());
			this.identifyOppositeRecipient(host).sendMessage(message);
		}
		else
			this.handleError(new TCGGeneralException("Server unavailable. From: receiveResultMessage"));
	}

	/**
	 * Send the start turn message to the opponent player.
	 * @param message	TCGMessage
	 * @param host	String
	 */
	public void receiveStartTurnMessage(TCGMessage message, String host)
	{
		if(this.server != null)
		{
			message.setMessageId(this.server.getMessageId());
			this.identifyRecipient(host).setStatus(ServerProtocol.HOST_WAITING);
			this.identifyOppositeRecipient(host).setStatus(ServerProtocol.HOST_ACTIVE);
			this.identifyOppositeRecipient(host).sendMessage(message);
		}
		else
			this.handleError(new TCGGeneralException("Server unavailable. From: receiveStartTurnMessage"));
	}
	/**
	 * Set the protocol's name
	 * @param String
	 */
	public void setProtocolName(String name)
	{
		this.name = name;
	}

	/**
	 * Set the server.
	 */
	public void setServer(TCGServer server)
	{
		this.server = server;
	}

}


